﻿using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Sustainalytics.Entities.RiskRatings;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Linq;
using System.Web.Http.Cors;
using System.Collections.Generic;
using Sustainalytics.ClientDW.Entities;

namespace Sustainalytics.RiskRatings.Service.Controllers.Company
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/company/{companyId}")]
    public class BenchmarkController : ApiController
    {
        private readonly IMongoCollection<RiskRatingBenchmark> benchMarkCollection;
        private readonly IMongoCollection<PeerAggregate> peerAggregateCollection;
        private readonly IMongoCollection<RiskRatingResearchProfile> profileCollection;
        private readonly IMongoCollection<CoreRiskRatingResearchProfile> profileCollectionCore;
        private readonly IMongoCollection<RiskRatingMaterialEsgIssue> meiCollection;
        private readonly IMongoCollection<CorporateData> _corporateDataCollection;

        public BenchmarkController(IMongoCollection<RiskRatingBenchmark> benchMarkCollection,
            IMongoCollection<PeerAggregate> peerAggregateCollection,
            IMongoCollection<RiskRatingResearchProfile> profileCollection,
            IMongoCollection<CoreRiskRatingResearchProfile> profileCollectionCore,
            IMongoCollection<RiskRatingMaterialEsgIssue> meiCollection,
            IMongoCollection<CorporateData> corporateDataCollection)
        {
            this.benchMarkCollection = benchMarkCollection;
            this.peerAggregateCollection = peerAggregateCollection;
            this.profileCollection = profileCollection;
            this.profileCollectionCore = profileCollectionCore;
            this.meiCollection = meiCollection;
            _corporateDataCollection = corporateDataCollection;
        }

        [HttpGet]
        [Route("benchmark/universe")]
        public async Task<HttpResponseMessage> GetCompanyUniverseBenchmark(Guid companyId)
        {

            var benchmark = await benchMarkCollection.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);

            if (benchmark == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            benchmark.CompanyBenchmark.UniversePerformanceRisk.Percentile = Math.Round(benchmark.CompanyBenchmark.UniversePerformanceRisk.Percentile * 100, MidpointRounding.AwayFromZero);

            return Request.CreateResponse(HttpStatusCode.OK, new
            {
                Performance = benchmark.CompanyBenchmark.UniversePerformanceRisk,
                ChartData = peerAggregateCollection.AsQueryable().OfType<UniverseAggregate>().FirstOrDefault().Data
            });

        }

        [HttpGet]
        [Route("benchmark/exposure/universe")]
        public async Task<HttpResponseMessage> GetUniverseExposure()
        {
            var comprehensiveCompanies = await profileCollection.Aggregate()
            .Project(x => new
            {
                CompanyId = x.Id,
                x.CompanyName,
                Exposure = x.ManagementRisk.Exposure.Value,
                Management = x.ManagementRisk.Management.Value,
                ManageableRisk = x.ManagementRisk.ManageableRisk.Value,
                ManagedRisk = x.ManagementRisk.ManagedRisk.Value,
                Cluster = x.Cluster.Code,
            })
            .ToListAsync();

            var coreCompanies = await profileCollectionCore.Aggregate()
                .Project(x => new
                {
                    CompanyId = x.Id,
                    x.CompanyName,
                    Exposure = x.ManagementRisk.Exposure.Value,
                    Management = x.ManagementRisk.Management.Value,
                    ManageableRisk = x.ManagementRisk.ManageableRisk.Value,
                    ManagedRisk = x.ManagementRisk.ManagedRisk.Value,
                    Cluster = x.Cluster.Code
                })
                .ToListAsync();


            return Request.CreateResponse(HttpStatusCode.OK, comprehensiveCompanies.Union(coreCompanies));
        }

        [HttpGet]
        [Route("benchmark/exposure/baseline")]
        //for baseline exposure
        public async Task<HttpResponseMessage> GetBaselineExposure()
        {
            var baseline = await meiCollection.Aggregate()
                .Match(x => x.Type == MaterialIssueType.BaselineIssue)
                .Project(x => new
                {
                    x.CompanyId,
                    x.CompanyName,
                    Cluster = x.Cluster.Code,
                    Exposure = x.ManagementRisk.Exposure.Value,
                    Management = x.ManagementRisk.Management.Value
                }).ToListAsync();

            return Request.CreateResponse(HttpStatusCode.OK, baseline);
        }

        [HttpGet]
        [Route("benchmark/exposure/mei/{externalId}")]
        //for mei exposure
        public async Task<HttpResponseMessage> GetMeiExposure(int externalId)
        {
            var mei = await meiCollection.Aggregate()
                .Match(x => x.ExternalId == externalId)
                .Project(x => new
                {
                    x.CompanyId,
                    x.CompanyName,
                    Cluster = x.Cluster.Code,
                    Exposure = x.ManagementRisk.Exposure.Value,
                    Management = x.ManagementRisk.Management.Value
                }).ToListAsync();

            return Request.CreateResponse(HttpStatusCode.OK, mei);
        }

        [HttpGet]
        [Route("benchmark/subindustry")]
        public async Task<HttpResponseMessage> GetCompanySubIndustryBenchmark(Guid companyId)
        {

            //get company subindustry 
            var companyRiskResearch = await profileCollection.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);
            var companyCoreRiskResearch = await profileCollectionCore.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);

            if ((companyRiskResearch == null) && (companyCoreRiskResearch == null))
                return Request.CreateResponse(HttpStatusCode.NotFound);

            IEnumerable<RiskRatingDataPoint> chartData = null;
            if (companyRiskResearch != null)
            { //company is from RiskRatingResearchProfile
                chartData = peerAggregateCollection.AsQueryable().OfType<SubIndustryAggregate>().Where(x => x.SubIndustryId == companyRiskResearch.SubPeerGroupId).FirstOrDefault().Data;
            }
            else
            { //company is from CoreRiskRatingResearchProfile
                chartData = peerAggregateCollection.AsQueryable().OfType<SubIndustryAggregate>().Where(x => x.SubIndustryId == companyCoreRiskResearch.SubPeerGroupId).FirstOrDefault().Data;
            }

            //get benchmark data for company subindustry
            var benchmark = await benchMarkCollection.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);

            if (benchmark == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            benchmark.CompanyBenchmark.SubIndustryPerformanceRisk.Percentile = Math.Round(benchmark.CompanyBenchmark.SubIndustryPerformanceRisk.Percentile * 100, MidpointRounding.AwayFromZero);

            return Request.CreateResponse(HttpStatusCode.OK, new
            {
                Performance = benchmark.CompanyBenchmark.SubIndustryPerformanceRisk,
                benchmark.CompanyBenchmark.PeersList,
                chartData
            });

        }

        [HttpGet]
        [Route("benchmark/industry")]
        public async Task<HttpResponseMessage> GetCompanyIndustryBenchmark(Guid companyId)
        {
            var corporateData = await _corporateDataCollection
                .Find(x => x.Id == companyId)
                .Project<CorporateData>(Builders<CorporateData>.Projection.Include(x => x.PeerGroup))
                .FirstOrDefaultAsync()
                .ConfigureAwait(false);


            if (corporateData == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            var chartData = peerAggregateCollection.AsQueryable().OfType<IndustryAggregate>().Where(x => x.Industry == corporateData.PeerGroup).FirstOrDefault().Data;

            //get benchmark data for company industry
            var benchmark = await benchMarkCollection.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);

            if (benchmark == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            benchmark.CompanyBenchmark.IndustryPerformanceRisk.Percentile = Math.Round(benchmark.CompanyBenchmark.IndustryPerformanceRisk.Percentile * 100, MidpointRounding.AwayFromZero);

            return Request.CreateResponse(HttpStatusCode.OK, new
            {
                Performance = benchmark.CompanyBenchmark.IndustryPerformanceRisk,
                benchmark.CompanyBenchmark.PeersList,
                chartData
            });

        }

        [HttpGet]
        [Route("benchmark/mei/{externalId}")]
        public async Task<HttpResponseMessage> GetCompanyMeiBenchmark(Guid companyId, int externalId)
        {

            var companyRiskResearch = await profileCollection
                .Find(x => x.Id == companyId)
                .Project<RiskRatingResearchProfile>(Builders<RiskRatingResearchProfile>.Projection
                .Include(x => x.PeerGroup)
                .Include(x => x.SubPeerGroupId))
                .FirstOrDefaultAsync()
                .ConfigureAwait(false);

            if (companyRiskResearch == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            //get mei data for company
            var chartDataUniverse = peerAggregateCollection.AsQueryable().OfType<UniverseMaterialEsgIssueAggregate>().Where(x => x.MEIExternalId == externalId).FirstOrDefault().Data;

            var chartDataIndustry = peerAggregateCollection.AsQueryable().OfType<IndustryMaterialEsgIssueAggregate>().Where(x => x.Industry == companyRiskResearch.PeerGroup && x.MEIExternalId == externalId).FirstOrDefault().Data;

            var chartDataSubIndustry = peerAggregateCollection.AsQueryable().OfType<SubIndustryMaterialEsgIssueAggregate>().Where(x => x.SubIndustryId == companyRiskResearch.SubPeerGroupId && x.MEIExternalId == externalId).FirstOrDefault().Data;

            var benchmark = await benchMarkCollection
                .Find(x => x.Id == companyId)
                .Project<RiskRatingBenchmark>(Builders<RiskRatingBenchmark>.Projection
                .Include(x => x.MaterialEsgIssueBenchmarks))
                .FirstOrDefaultAsync()
                .ConfigureAwait(false);

            if (benchmark == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            return Request.CreateResponse(HttpStatusCode.OK, new
            {
                UniversePerformance = benchmark.MaterialEsgIssueBenchmarks.Where(x => x.MeiExternalId == externalId).Select(x => x.UniversePerformanceRisk).FirstOrDefault(),
                IndustryPerformance = benchmark.MaterialEsgIssueBenchmarks.Where(x => x.MeiExternalId == externalId).Select(x => x.IndustryPerformanceRisk).FirstOrDefault(),
                SubIndustryPerformance = benchmark.MaterialEsgIssueBenchmarks.Where(x => x.MeiExternalId == externalId).Select(x => x.SubIndustryPerformanceRisk).FirstOrDefault(),
                PeersList = benchmark.MaterialEsgIssueBenchmarks.Where(x => x.MeiExternalId == externalId).Select(x => x.PeersList).FirstOrDefault(),
                chartDataUniverse,
                chartDataIndustry,
                chartDataSubIndustry
            });

        }


        [HttpGet]
        [Route("benchmark/exposure")]
        public async Task<HttpResponseMessage> GetCompanySubIndustryExposure(Guid companyId)
        {
            var corporateData = await _corporateDataCollection
              .Find(x => x.Id == companyId)
              .Project<CorporateData>(Builders<CorporateData>.Projection.Include(x => x.SubPeerGroupId))
              .FirstOrDefaultAsync()
              .ConfigureAwait(false);

            if (corporateData == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            var comprehensiveCompanies = await profileCollection.Aggregate()
            .Match(x => x.SubPeerGroupId == corporateData.SubPeerGroupId)
            .Project(x => new
            {
                CompanyId = x.Id,
                x.CompanyName,
                Exposure = x.ManagementRisk.Exposure.Value,
                Management = x.ManagementRisk.Management.Value,
                ManageableRisk = x.ManagementRisk.ManageableRisk.Value,
                ManagedRisk = x.ManagementRisk.ManagedRisk.Value,
                Cluster = x.Cluster.Code
            })
            .ToListAsync();

            var coreCompanies = await profileCollectionCore.Aggregate()
                .Match(x => x.SubPeerGroupId == corporateData.SubPeerGroupId)
                .Project(x => new
                {
                    CompanyId = x.Id,
                    x.CompanyName,
                    Exposure = x.ManagementRisk.Exposure.Value,
                    Management = x.ManagementRisk.Management.Value,
                    ManageableRisk = x.ManagementRisk.ManageableRisk.Value,
                    ManagedRisk = x.ManagementRisk.ManagedRisk.Value,
                    Cluster = x.Cluster.Code
                })
                .ToListAsync();


            return Request.CreateResponse(HttpStatusCode.OK, comprehensiveCompanies.Union(coreCompanies));
        }

        [HttpGet]
        [Route("benchmark/mei/{externalId}/exposure")]
        public async Task<HttpResponseMessage> GetMeiSubIndustryExposure(Guid companyId, int externalId)
        {

            //get company subindustry 
            var companyRiskResearch = await profileCollection.Find(x => x.Id == companyId).FirstOrDefaultAsync().ConfigureAwait(false);
            if (companyRiskResearch == null)
                return Request.CreateResponse(HttpStatusCode.NotFound);

            return Request.CreateResponse(HttpStatusCode.OK,
                meiCollection.Aggregate()
                .Match(x => x.SubPeerGroupId == companyRiskResearch.SubPeerGroupId && x.ExternalId == externalId)
                .Project(x => new
                {
                    CompanyId = x.CompanyId,
                    x.CompanyName,
                    Exposure = x.ManagementRisk.Exposure.Value,
                    Management = x.ManagementRisk.Management.Value,
                    Cluster = x.Cluster.Code
                })
                .ToList()
            );

        }

        //TODO: to be optimized because some of the methods don't need companyId, but subPeerGroupId
    }
}
